#ifndef FILE_SORTER_HPP_
#define FILE_SORTER_HPP_
#define MAX_VALUE_LENGTH 10

//sort method aggregates all bussiness logic, analyzing data via iterator, parsing,
//saving data to /temp/files
class FileSorter {
	private:
		File *infile, *outfile;
		std::vector<File*> chunks; //temp files with data
		
		char* a_page_slice;				//append in the begining slice
		char* e_page_slice;				//append in the end slice
		unsigned int a_slice_length;	//length of appending slice
		unsigned int e_slice_length;	//length of ending slice

		inline int is_digit(char c) {
			return (c <= '9' && c >= '0'); 
		}
		//method pushes back char, allocating memory if @word is empty
		void word_push_back(char ** word, int size, char c) {
			if (size == 0) {
				*word = (char*)malloc(sizeof(char) * (MAX_VALUE_LENGTH + 1));
			}
			(*word)[size] = c;
			(*word)[size + 1] = '\0';
		}
		//method copies the size_n bytes from src to dest, equiv strncpy - which is not safe
		//watch -- man strncpy
		void string_copy(char* dest, char* src, unsigned int size_n) {
			for (unsigned int i = 0; i < size_n; ++i) {
				dest[i] = src[i];
			}
		}
		//method returns vector<int> from parsed part, appending @append_slice in the begining
		//and cutting appendix of @part from the end, @to end_slice_length
		std::vector<int> csv_parser(char *part, unsigned int size, char* append_slice, 
				unsigned int append_slice_length, unsigned int end_slice_length) {

			std::vector<int> output;
			unsigned int position = 0;
			unsigned int end_position = size - end_slice_length + append_slice_length;
			int word_size = 0,
				finalized = 0;
			char *number = NULL;
			while (position < end_position) {
				char ch;
				if (position < append_slice_length)
					ch = append_slice[position];
				else 
					ch = part[position - append_slice_length];
				if (is_digit(ch) && (word_size < MAX_VALUE_LENGTH)) {
					word_push_back(&number, word_size++, ch);
				} else {
					if (word_size > MAX_VALUE_LENGTH) {
							//TODO make loogger
							//handle_error("max input value size exceeded");
						finalized = 0;
						word_size = 0;
						free(number);		
					}
					switch (ch) {
						case '-' :  if (word_size == 0) {
										word_push_back(&number, word_size++, ch);
									}		
									//WARNING:"parseCSVToInt failed, maybe corrupted data(%s)		
									break;
						case ',' :	if (word_size == 0) {
									//WARNING:"parseCSVToInt failed, maybe corrupted data(%s)
									} else {
										finalized = 1;
									}
									break;
						default  :  //WARNING:"parseCSVToInt failed, maybe corrupted data(%s)
									break;
					}
				}
				if (finalized || (position == end_position - 1)) {
					char *end_ptr;
					int num_value = (int)strtol(number, &end_ptr, 10);
					if ((errno == ERANGE && (num_value == INT_MAX || num_value == INT_MIN))
						|| (errno != 0 && num_value == 0) || (end_ptr == number)) {
						//LOGGER - WARNING: bad strtol
					} else {
						output.push_back(num_value);
					}
					finalized = 0;
					word_size = 0;
					free(number);
				}
				++position;
			}
			return output;
		}
		//method returns pointer to end slice of mapped area(from last ',' up the end)
		//returns NULL if e_slice_length == 0, e_slice_length is set to 0
 		char* slice(char* part, unsigned int size) {
			unsigned int counter = size - 1;
			char *output = NULL;
			while (part[counter] != ',') {
				--counter;
			}
			e_slice_length = size - counter - 1;
			if (e_slice_length > 0) {
				output = (char*)malloc(sizeof(char) * e_slice_length);//one byte to write '\0'
				string_copy(output, &part[counter + 1], e_slice_length);
			}
			return output;
		}
	public:
		FileSorter(char* input, char* output) {
			infile = new File(input, true, -1);
			outfile = new File(output, false, -1);
			e_slice_length = 0;
			a_slice_length = 0;
			e_page_slice = NULL;
			a_page_slice = NULL;
		}
		//method creates @FileIterator and runs over @FileSorter::infile
		//after each FileIterator::next(), mapped area is parsed, sorted and written
		//to /tmp file , created with mkstemp, pointer to tmpfile added to @FileSorter::chunks
		//after @FileMerger created and merges all tmp files in a standart way
		void sort() {
			FileIterator itInput = FileIterator(*infile);
			while (itInput.has_next()) {
 				char* part = itInput.next();
 				if (part == NULL) {
 					//TODO
 					//finsh programm, close descriptors, free memory etc.
 					//maybe destructors will do that for me, I hope
 					perror("iterator mapping failed");
 					return;
 				}
				e_page_slice = slice(part, itInput.get_mapped_size());
				std::vector<int> chunk_vec = csv_parser(part, itInput.get_mapped_size(), a_page_slice, 
					a_slice_length, e_slice_length);
					if (!itInput.has_next() && (e_page_slice != NULL)) {	//insert last number
						char* end_ptr;
						int num_value = (int)strtol(e_page_slice, &end_ptr, 10);
						if ((errno == ERANGE && (num_value == INT_MAX || num_value == INT_MIN))
							|| (errno != 0 && num_value == 0) || (end_ptr == e_page_slice)) {
							//LOGGER - WARNING PRINT bad strtol
						} else {
							chunk_vec.push_back(num_value);
						}
					}
				if (a_page_slice != NULL) {
					free(a_page_slice);
					a_page_slice = NULL;
				}
				a_page_slice = e_page_slice;
				a_slice_length = e_slice_length;

				std::sort(chunk_vec.begin(), chunk_vec.end());
				char filename[] = "/tmp/temp_file.XXXXXX";
				int fd = mkstemp(filename);
				File * ftmp = new File(filename, false, fd);
				chunks.push_back(ftmp);
				for (std::vector<int>::iterator it = chunk_vec.begin(); it != chunk_vec.end(); ++it) {
					File::write_stream_integer(*ftmp, *it);
				}
				//set stream to beginning in order to read data
				if (fseek(ftmp -> get_stream(), 0, SEEK_SET) == -1) {
					perror("fseek tmp file failed");
					return;
				}
			}
			FileMerger * merger = new FileMerger(chunks, outfile);
			(*merger).merge();
			delete(merger);
			free(a_page_slice);
		}
		~FileSorter() {
			delete(infile);
			delete(outfile);
			for (std::vector<File*>::iterator it = chunks.begin(); it != chunks.end(); ++it) {
					delete(*it);
			}
		}
};
#endif
